#include <QtCore>
#include <QtWidgets>
#include <QSqlQuery>
#include "Comm/SmartFrame.h"
#include "Comm/errorcode.h"
#include "Comm/GlobalSetting.h"
#include "Comm/TaskMan.h"
#include "CommFunc.h"
#include "SendGroup.h"
#include "Database/ddm.h"
#include "IHPLApp.h"
#include "ui_IHPLWidget.h"

//---------------------------------------------------------------------------------------
#define TRANS_HEADER    0x0
#define TRANS_FINISHED  0xFFFF
//---------------------------------------------------------------------------------------
enum State{UPLOAD_RUNING, UPLOAD_STOPING, DOWNLOAD_RUNING, IDLE};
enum {COL_AID, COL_STATUS};
//---------------------------------------------------------------------------------------
class IHPLApp::PrivateData
{
public:
    uint32_t cur_seq;
    uint32_t dest;
    int state, resend_cnt;

    uint32_t lib_blocknum;
    uint32_t lib_size;
    uint16_t lib_crc;
    uint8_t blocksize;
    uint16_t libno;

    uint32_t cache_idx;
    uint8_t cache[0x100];

    int cur_row;
    QByteArray lib;
};
//---------------------------------------------------------------------------------------
IHPLApp::IHPLApp(TaskMan *task_man, QWidget *parent) :
    BaseApp(parent), ui(new Ui::IHPLWidget), task_man(task_man)
{
    ui->setupUi(this);

    setting = GlobalSetting::GetInstance();

    task = new Task();
    d_data = new PrivateData();

    d_data->state = IDLE;

    QString str = "二进制文件(*.bin)";
    ui->lineEdit_path->SetActionType(LineEditWithAction::GetSaveFile, &str);
}

IHPLApp::~IHPLApp()
{
    delete ui;
    delete task;
    delete d_data;
}
//---------------------------------------------------------------------------------------
void IHPLApp::update_ui(void)
{
    switch (d_data->state)
    {
    case IDLE:
        ui->groupBox_upload->setEnabled(true);
        ui->tableWidgetDevList->setEnabled(true);
        ui->pushButton_upload->setText("提取");
        ui->pushButton_start->setText("开始");
        break;
    case UPLOAD_RUNING:
        ui->groupBox_upload->setEnabled(false);
        ui->pushButton_upload->setText("停止");
        break;
    case DOWNLOAD_RUNING:
        ui->tableWidgetDevList->setEnabled(false);
        ui->pushButton_start->setText("停止");
        break;
    }
}
//---------------------------------------------------------------------------------------
void IHPLApp::on_pushButton_upload_clicked()
{
    int err = SMART_ENO;
    uint8_t libno[2] = {0};

    if (d_data->state == UPLOAD_RUNING)
    {
        d_data->state = IDLE;
        QMessageBox::critical(this, "错误", "提取失败");
        goto out;
    }

    if (ui->lineEdit_target->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请设置设备地址");
        goto out;
    }

    if (ui->lineEdit_path->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择码库文件存储路径");
        goto out;
    }

    d_data->dest = ui->lineEdit_target->text().toUInt();
    d_data->cur_seq = 0;
    d_data->resend_cnt = 0;
    task->dlen = code_trans_packet(true, libno, sizeof(libno),
                                   task->data, sizeof(task->data));
    task->args = d_data;
    task->taskInf = this;
    err = task_man->AddTask(task);
    if (err < 0)
    {
        QMessageBox::critical(this, "错误", QString(SmartError(-err)));
        goto out;
    }
    d_data->state = UPLOAD_RUNING;

out:
    update_ui();
}
//---------------------------------------------------------------------------------------
int IHPLApp::code_trans_packet(bool upload,
                               uint8_t *data, int len,
                               uint8_t *out, int maxlen)
{
    uint8_t tmp[0x200];

    struct LibHead *lib = (struct LibHead *)tmp;

    memset(lib, 0, LIBHEAD_SIZE);
    put_le_val(d_data->cur_seq, lib->seq, sizeof(lib->seq));
    lib->ack = 1;

    memcpy(lib->data, data, len);

    int cmd = upload ? CMD_READ : CMD_SET;

    return CodeUnicastFrame(setting->master_id, d_data->dest, cmd, DID_IRLIB,
                            lib, LIBHEAD_SIZE + len, out, maxlen);
}
//---------------------------------------------------------------------------------------
void IHPLApp::get_lib_header(struct LibHead *head, int len)
{
    Q_UNUSED(len);

    struct LibHeader *lib = (struct LibHeader *)head->data;

    d_data->lib_size = get_le_val(lib->len, sizeof(lib->len));
    d_data->blocksize =lib->blocksize;
    d_data->lib_crc = get_le_val(lib->crc, sizeof(lib->crc));
    d_data->lib_blocknum = (d_data->lib_size + d_data->blocksize - 1) / d_data->blocksize;

    d_data->lib.clear();
}
void IHPLApp::get_lib_data(struct LibHead *head, int len)
{
    d_data->lib.append((const char*)head->data, len);
}
void IHPLApp::get_lib_tail(void)
{
    QFile f(ui->lineEdit_path->text());

    if (!f.open(QIODevice::WriteOnly)) return;
    f.write(d_data->lib);
    f.close();
}

void IHPLApp::OnUploadTaskDone(PrivateData *arg, Task *task)
{
    if (d_data->state != UPLOAD_RUNING) return;

    QString msg("提取失败");

    if (task->dlen > 0)
    {
        struct SmartFrame *frame = get_smart_frame(task->data, task->dlen);

        const struct Body *body = GetBody(frame, DID_IRLIB);
        struct LibHead *head = (struct LibHead *)body->data;
        uint32_t seq = get_le_val(head->seq, sizeof(head->seq));
        if (d_data->cur_seq == seq)
        {
            void (IHPLApp::*handler)(struct LibHead *, int) =
                seq == 0 ? &IHPLApp::get_lib_header : &IHPLApp::get_lib_data;
            (this->*handler)(head, get_bits(body->ctrl, 0, 6) - LIBHEAD_SIZE);

            if (++d_data->cur_seq > d_data->lib_blocknum)
            {
                d_data->cur_seq = TRANS_FINISHED;
                get_lib_tail();
            }
        }
        task->dlen = code_trans_packet(true, NULL, 0,
                                       task->data, sizeof(task->data));
        if (task_man->AddTask(task) < 0) goto done;

        if (d_data->cur_seq == TRANS_FINISHED)
        {
            msg = "提取完成";
            goto done;
        }
        return;
    }
    else if (task->dlen == -SMART_ETMOUT)
    {
        if (++arg->resend_cnt < 3)
        {
            uint8_t libno[2] = {0};
            task->dlen = code_trans_packet(true, libno, sizeof(libno),
                                           task->data, sizeof(task->data));
            if (task_man->AddTask(task) < 0) goto done;
            return;
        }
    }

done:
    arg->state = IDLE;
    update_ui();
    QMessageBox::information(this, "信息", msg);
}
//---------------------------------------------------------------------------------------
void IHPLApp::OnTaskDone(Task *task)
{
    PrivateData *arg = (PrivateData *)task->args;

    switch (arg->state)
    {
    case DOWNLOAD_RUNING:
        OnDownloadTaskDone(arg, task);
        break;
    case UPLOAD_RUNING:
        OnUploadTaskDone(arg, task);
        break;
    default: break;
    }
}
//---------------------------------------------------------------------------------------
void IHPLApp::ClearTableWidget(void)
{
    int rows = ui->tableWidgetDevList->rowCount();

    while (rows--)
    {
        ui->tableWidgetDevList->removeRow(0);
    }
}
//---------------------------------------------------------------------------------------
void IHPLApp::on_pushButtonSelAll_clicked()
{
    int rows = ui->tableWidgetDevList->rowCount();

    while (rows--)
    {
        QCheckBox *checkBox = dynamic_cast<QCheckBox*>(
                    ui->tableWidgetDevList->cellWidget(rows, COL_AID));
        checkBox->setChecked(true);
    }
}

void IHPLApp::on_pushButtonDeSel_clicked()
{
    int rows = ui->tableWidgetDevList->rowCount();

    while (rows--)
    {
        QCheckBox *checkBox = dynamic_cast<QCheckBox*>(
                    ui->tableWidgetDevList->cellWidget(rows, COL_AID));
        checkBox->setChecked(!checkBox->isChecked());
    }
}
//---------------------------------------------------------------------------------------
void IHPLApp::on_pushButton_import_clicked()
{
    QSqlQuery q;

    QString sql(QObject::tr("SELECT AID FROM SmartDevices"));
    qDebug() << sql << endl;
    if (!q.exec(sql)) {
        qDebug() << DDMError() << endl;
        return;
    }
    ClearTableWidget();

    int row = 0;
    while (q.next())
    {
        ui->tableWidgetDevList->insertRow(row);
        ui->tableWidgetDevList->setCellWidget(row, COL_AID,
                                              new QCheckBox(q.value(0).toString()));
        ui->tableWidgetDevList->setItem(row, COL_STATUS, new QTableWidgetItem("0/0"));
        row++;
    }
    on_pushButtonSelAll_clicked();
}
//---------------------------------------------------------------------------------------
int IHPLApp::GetNextRow(int from)
{
    int rows = ui->tableWidgetDevList->rowCount();

    for (int i = from; i < rows; i++)
    {
        QCheckBox *checkBox = dynamic_cast<QCheckBox*>(
                    ui->tableWidgetDevList->cellWidget(i, COL_AID));
        if (checkBox->isChecked())
        {
            return i;
        }
    }
    return -1;
}
//---------------------------------------------------------------------------------------
int IHPLApp::ParseLibFile(const QString &path)
{
    QFile f(path);

    if (!f.open(QIODevice::ReadOnly)) return -1;
    d_data->lib = f.readAll();
    f.close();
    return 0;
}
int IHPLApp::get_download_header(uint8_t *out, int maxlen)
{
    Q_UNUSED(maxlen);
    struct LibHeader *h = (struct LibHeader *)out;

    put_le_val(d_data->lib.size(), h->len, sizeof(h->len));
    uint16_t crc = calc_crc16(0, d_data->lib.data(), d_data->lib.size());
    put_le_val(crc, h->crc, sizeof(h->crc));
    h->blocksize = IRLIB_SEND_BLOCKSIZE;
    put_le_val(0, h->libno, sizeof(h->libno));

    d_data->blocksize = IRLIB_SEND_BLOCKSIZE;
    d_data->lib_blocknum = (d_data->lib.size() + d_data->blocksize - 1)/d_data->blocksize;

    return sizeof(struct LibHeader);
}
int IHPLApp::get_download_data(int seq, uint8_t *out, size_t maxlen)
{
    if (seq <= 0) return -1;

    const QByteArray &b = d_data->lib;

    seq--;
    if (d_data->lib.count() < d_data->blocksize*seq) return -1;

    size_t len = min(d_data->blocksize, b.count() - d_data->blocksize*seq);
    if (maxlen < len) return -1;

    memcpy(out, b.data()+d_data->blocksize*seq, len);
    return len;
}
int IHPLApp::get_download_packet(int seq, uint8_t *out, size_t maxlen)
{
    if (seq == 0) return get_download_header(out, maxlen);

    return get_download_data(seq, out, maxlen);
}

int IHPLApp::AddDownLoadTask(PrivateData *arg, int seq, bool nextRow)
{
    uint8_t tmp[0x200];
    int len = 0;

    if (nextRow)
    {
        arg->cur_row = GetNextRow(arg->cur_row+1);
        if (arg->cur_row < 0) return 1;
        QCheckBox *checkBox = dynamic_cast<QCheckBox*>(
                ui->tableWidgetDevList->cellWidget(arg->cur_row, COL_AID));
        arg->dest = checkBox->text().toInt();
        d_data->resend_cnt = 0;
        seq = 0;
    }

    len = get_download_packet(seq, tmp, sizeof(tmp));
    if (len < 0) return -SMART_ENODATA;
    task->dlen = code_trans_packet(false, tmp, len,
                                   task->data, sizeof(task->data));
    d_data->cur_seq = seq;
    task->args = arg;
    task->taskInf = this;
    return task_man->AddTask(task);
}
void IHPLApp::on_pushButton_start_clicked()
{
    if (d_data->state == DOWNLOAD_RUNING)
    {
        d_data->state = IDLE;
        update_ui();
        return;
    }

    if (ui->lineEdit_path->text().isEmpty())
    {
        QMessageBox::critical(this, "错误", "请选择码库文件存储路径");
        return;
    }

    if (ParseLibFile(ui->lineEdit_path->text()) < 0) return;

    d_data->cur_row = -1;
    d_data->cur_seq = 0;
    int err = AddDownLoadTask(d_data, 0, true);
    if (err == 0)
    {
        d_data->state = DOWNLOAD_RUNING;
        update_ui();
        return;
    }

    if (err > 0)
    {
        QMessageBox::critical(this, "错误", "请选择升级设备");
    }
    else if (err < 0)
    {
        QMessageBox::critical(this, "错误", QString(SmartError(-err)));
    }
}
void IHPLApp::OnDownloadTaskDone(PrivateData *arg, Task *task)
{
    bool nextRow = false;
    int err = task->dlen;

    if (d_data->state == DOWNLOAD_RUNING)
    {
        QTableWidgetItem *item = ui->tableWidgetDevList->item(arg->cur_row, COL_STATUS);
        if (task->dlen > 0)
        {
            struct SmartFrame *frame = get_smart_frame(task->data, task->dlen);
            const struct Body *body = GetBody(frame, DID_IRLIB);
            struct LibHead *head = (struct LibHead *)body->data;

            int seq = get_le_val(head->seq, sizeof(head->seq));
            switch (seq)
            {
            case 0: item->setText("下载失败"); nextRow = true; break;
            case TRANS_FINISHED: nextRow = true; break;
            default: d_data->resend_cnt = 0; d_data->cur_seq = seq;
                item->setText(QString("%1/%2").arg(d_data->cur_seq).arg(d_data->lib_blocknum));
                break;
            }
        }
        else if (task->dlen == -SMART_ETMOUT)
        {
            d_data->resend_cnt++;
            nextRow = d_data->resend_cnt < 3 ? false : true;
            if (nextRow) item->setText("下载失败");
        }
        err = AddDownLoadTask(arg, d_data->cur_seq, nextRow);
    }
    if (err == 0) return;

    d_data->state = IDLE;
    update_ui();
    QMessageBox::information(this, "信息", QString("下载完成!!!"));
}
//---------------------------------------------------------------------------------------
BaseApp *CreateIHPLApp(QWidget *parent, TaskMan *task_man)
{
    return new IHPLApp(task_man, parent);
}
//---------------------------------------------------------------------------------------
